package com.zboot.comm.redis.ann;

import com.zboot.comm.redis.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 缓存切面
 *
 * @author scihi
 */
@Aspect
@Component
@Slf4j
public class RedisCacheAspect {

    @Autowired
    private RedisService redisCache;

    // 缓存数据切面切面
    @Pointcut("@annotation(com.zboot.comm.redis.ann.EnableRedisCache)")
    public void EnableRedisCache() {
    }

    // 刷新缓存切面
    @Pointcut("@annotation(com.zboot.comm.redis.ann.EnableRedisCacheFresh)")
    public void EnableRedisCacheFresh() {
    }

    @Around("EnableRedisCache()")
    public Object saveCache(ProceedingJoinPoint point) throws Throwable {
        EnableRedisCache ann = this.getEnableRedisCache(point);
        if(ann==null){
            return point.proceed();
        }
        Object[] args = point.getArgs();//参数列表
        Method method = getTargetMethod(point);
        String methodName = getClassAndMethodName(method);
        String hashKey = methodName + "(";
        for (int i=0; i<args.length; i++) {
            if(i<args.length-1){
                hashKey += args[i] + ", ";
            }else{
                hashKey += args[i] + ")";
            }
        }
        Object obj = redisCache.getCacheMapValue(ann.value(), hashKey);
        if(obj!=null){//如果缓存有效
            log.debug(">>>> 命中缓存：{} - {}", ann.value(), hashKey);
            return obj;
        }else{//缓存无效
            obj = point.proceed();//执行方法
            if(obj!=null){
                log.debug(">>>> 写入缓存：{} - {}", ann.value(), hashKey);
                redisCache.setCacheMapValue(ann.value(), hashKey, obj);//写入缓存
            }
            return obj;
        }
    }

    @AfterReturning(pointcut = "EnableRedisCacheFresh()", returning = "jsonResultt")
    public void freshCache(JoinPoint point, Object jsonResultt) {
        EnableRedisCacheFresh ann = this.getEnableRedisCacheFresh(point);
        if(ann==null){
            return;
        }
        log.debug(">>>> 清除缓存：{}", ann.value());
        redisCache.deleteObject(ann.value());
    }

    private EnableRedisCache getEnableRedisCache(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null) {
            return method.getAnnotation(EnableRedisCache.class);
        }
        return null;
    }

    private EnableRedisCacheFresh getEnableRedisCacheFresh(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null) {
            return method.getAnnotation(EnableRedisCacheFresh.class);
        }
        return null;
    }

    //基于连接点信息获取目标方法对象
    private Method getTargetMethod(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
        //获取目标类对象
        Class<?> aClass = joinPoint.getTarget().getClass();
        //获取方法签名信息,方法名和参数列表
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取目标方法对象
        Method method = aClass.getDeclaredMethod(signature.getName(), signature.getParameterTypes());
        return method;
    }
    //获取方法类全名+方法名
    private String getClassAndMethodName(Method method){
        //获取类全名
        String className = method.getDeclaringClass().getName();
        //获取方法名
        String methodName = method.getName();
        return new StringBuffer(className).append(".").append(methodName).toString();
    }
}
